package board;

import java.util.HashMap;
import java.util.Map;

public class Board
{
	private static final int EMPTY_CELL = -1;
	private static final int PLAYER1 = 0;
	private static final int PLAYER2 = 1;
	
	private Map<Coordinate, Integer> cells = new HashMap<Coordinate, Integer>();
	private Coordinate previousMove;
	
	public Board()
	{
		
	}
	
	/**
	 * Returns the coordinates of the previous move.
	 * Returns null if no move has been made.
	 */
	
	public Coordinate getPreviousMove()
	{
		return previousMove;
	}
	
	/**
	 * Returns the value of the specified cell.
	 */
	
	public int getCell(int x, int y)
	{
		Coordinate key = new Coordinate(x, y);
		
		if (cells.containsKey(key))
			return cells.get(key);
		else
			return EMPTY_CELL;
	}
	
	/**
	 * Sets the specified cell to the value provided.
	 */
	
	public void setCell(int x, int y, int value) throws IllegalArgumentException
	{
		Coordinate key = new Coordinate(x, y);
		
		if (cells.containsKey(key) || (value != PLAYER1 && value != PLAYER2))
			throw new IllegalArgumentException();
		else
		{
			cells.put(key, value);
			previousMove = key;
		}
	}
	
	/**
	 * Returns a two dimensional array representation of the board.
	 */
	
	public int[][] getBoard()
	{
		int minX = 0, maxX = 0, minY = 0, maxY = 0;
		
		for (Coordinate coord : cells.keySet())
		{
			if (coord.x < minX)
				minX = coord.x;
			if (coord.x > maxX)
				maxX = coord.x;
			if (coord.y < minY)
				minY = coord.y;
			if (coord.y > maxY)
				maxY = coord.y;
		}
		
		int width = 1 + maxX - minX;
		int height = 1 + maxY - minY;
		
		int[][] board = new int[height][width];
		
		for (int i = 0; i < board.length; i++)
		{
			for (int j = 0; j < board[i].length; j++)
			{
				int x = j + minX;
				int y = maxY - i;

				board[i][j] = getCell(x, y);
			}
		}
		
		return board;
	}
	
	/**
	 * Returns a string representation of the board.
	 * @see java.lang.Object#toString()
	 */
	
	@Override
	public String toString()
	{
		StringBuilder board = new StringBuilder();
		
		int minX = 0, maxX = 0, minY = 0, maxY = 0;
		
		for (Coordinate coord : cells.keySet())
		{
			if (coord.x < minX)
				minX = coord.x;
			if (coord.x > maxX)
				maxX = coord.x;
			if (coord.y < minY)
				minY = coord.y;
			if (coord.y > maxY)
				maxY = coord.y;
		}
		
		int width = 1 + maxX - minX;
		int height = 1 + maxY - minY;
		
		for (int i = 0; i < height; i++)
		{
			for (int j = 0; j < width; j++)
			{
				int x = j + minX;
				int y = maxY - i;
				
				board.append(getCell(x, y));
				board.append(' ');
			}
			
			board.append("\n");
		}
		
		return board.toString();
	}
	
	private class Coordinate
	{
		private int x;
		private int y;

		public Coordinate(int x, int y)
		{
			this.x = x;
			this.y = y;
		}
		
		/**
		 * Returns true if the other object is "equal to" this one.
		 * @see java.lang.Object#equals(java.lang.Object)
		 */
		
		@Override
		public boolean equals(Object obj)
		{
			if(this == obj)
				return true;
			
			if((obj == null) || (obj.getClass() != this.getClass()))
				return false;
			
			Coordinate test = (Coordinate) obj;
			
			return this.x == test.x && this.y == test.y;
		}
		
		/**
		 * Returns a hash code value, a distinct integer, for the object.
		 * @see java.lang.Object#hashCode()
		 */
		
		@Override
		public int hashCode()
		{
			int hashCode = 0;
			
			if (x == 0 && y == 0)
				return hashCode;
			
			int maxCoordinate = Math.max(Math.abs(x), Math.abs(y));
			int min = (int) Math.pow((maxCoordinate - 1) * 2 + 1, 2);
			int side = maxCoordinate * 2;
			int displacement = 0;
			
			if (x < maxCoordinate && y == maxCoordinate)
			{
				displacement = Math.abs(x + maxCoordinate);
			}
			else if (y > -maxCoordinate && x == maxCoordinate)
			{
				min += side;
				displacement = Math.abs(y - maxCoordinate);
			}
			else if (x > -maxCoordinate && y == -maxCoordinate)
			{
				min += 2 * side;
				displacement = Math.abs(x - maxCoordinate);
			}
			else if (y < maxCoordinate && x == -maxCoordinate)
			{
				min += 3 * side;
				displacement = Math.abs(y + maxCoordinate);
			}
			
			hashCode = min + displacement;
			return hashCode;
		}
		

		/**
		 * Returns a string representation of the object.
		 * @see java.lang.Object#toString()
		 */
		
		@Override
		public String toString()
		{
			return "(" + x + ", " + y + ")";
		}
	}
}
